package com.hhss.qishi.config.security;

import com.alibaba.fastjson.JSONObject;
import com.hhss.qishi.config.kaptcha.KaptchaVerfiyCodeFilter;
import com.hhss.qishi.config.log.Log;
import com.hhss.qishi.entity.system.UserSecurity;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 权限验证配置
 * @author Administrator
 *
 *form-login属性详解
	form-login是spring security命名空间配置登录相关信息的标签,它包含如下属性：
	1. login-page 自定义登录页url,默认为/login
	2. login-processing-url 登录请求拦截的url,也就是form表单提交时指定的action
	3. default-target-url 默认登录成功后跳转的url
	4. always-use-default-target 是否总是使用默认的登录成功后跳转url
	5. authentication-failure-url 登录失败后跳转的url
	6. username-parameter 用户名的请求字段 默认为userName
	7. password-parameter 密码的请求字段 默认为password
	8. authentication-success-handler-ref 指向一个AuthenticationSuccessHandler用于处理认证成功的请求,不能和default-target-url还有always-use-default-target同时使用
	9. authentication-success-forward-url 用于authentication-failure-handler-ref
	10. authentication-failure-handler-ref 指向一个AuthenticationFailureHandler用于处理失败的认证请求
	11. authentication-failure-forward-url 用于authentication-failure-handler-ref
	12. authentication-details-source-ref 指向一个AuthenticationDetailsSource,在认证过滤器中使用
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
	@Log
	Logger logger;

	// required = false 用户忽略idea的自动注入报错
	@Autowired(required = false)
    private AuthenticationProvider provider;
	
	@Override
    protected void configure(HttpSecurity http) throws Exception {
		http
		// 添加过滤器
				//验证码过滤器
		.addFilterBefore(new KaptchaVerfiyCodeFilter(new AntPathRequestMatcher("/login/loginIn", "POST")), 
				UsernamePasswordAuthenticationFilter.class)
		.authorizeRequests()
			// 所有用户均可访问的资源
			.antMatchers("/pages/login/**", "/login/**", "/pages/setup/**", "/setup/**", "/verifyCode/**", "/index", "/pages/sdd/**").permitAll()
				/*.antMatchers("/pages/**", "/main/**").hasRole("USER")*/
			// 任何尚未匹配的URL只需要验证用户即可访问
            .anyRequest().authenticated()
		.and()
			// 关闭CSRF,跨站伪造防护
			.csrf().disable()
		.formLogin()
			// 指定登录页面,授予所有用户访问登录页面
			.loginPage("/login").loginProcessingUrl("/login/loginIn")
			.usernameParameter("loginId").passwordParameter("password")
			.successHandler(loginSuccessHandler())
			//.defaultSuccessUrl("/main") // 成功登陆后跳转页面
            .failureHandler(loginFailureHander())
			//.failureUrl("/loginError")
			.permitAll()
		.and()
			.logout().permitAll().invalidateHttpSession(true)
        	.deleteCookies("JSESSIONID").logoutSuccessHandler(logoutSuccessHandler())
        .and()
        	// Spring Security默认将header response里的X-Frame-Options属性设置为DENY;
        	// 这里修改为sameOrigin（同源）
        	.headers().frameOptions().sameOrigin()
        	;
		
    }
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// user验证入口
		auth.authenticationProvider(provider);
	}
	
	/**
	 * .静态资源的设置
	 */
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("**.js", "**.css", "/css/**", "/js/**", "/json/**", "/images/**",
				"/annexe/**", "/statics/**", "/surfaces/**", "/surface/**", "/**/favicon.ico");
	}
	
	/**
	 * loginSuccessHandler 登入成功处理
	 * @return
	 */
	@Bean
	public SavedRequestAwareAuthenticationSuccessHandler loginSuccessHandler() {
		return new SavedRequestAwareAuthenticationSuccessHandler() {
			@Override
			public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
					Authentication authentication) throws IOException, ServletException {
				//什么都不做的话，那就直接调用父类的方法
	            //super.onAuthenticationSuccess(request, response, authentication);  
	            
				UserSecurity userDetails = (UserSecurity) authentication.getPrincipal();
				logger.info("USER : " + userDetails.getUsername() + " LOGIN SUCCESS !  ");
	            //这里可以根据实际情况，来确定是跳转到页面或者json格式。
	            //如果是返回json格式，那么我们这么写
				JSONObject result = new JSONObject();
	            result.put("returnCode", "200");
	            result.put("returnMsg", "./main");
	            response.setContentType("application/json;charset=UTF-8");
	            response.setCharacterEncoding("UTF-8");
	            response.getWriter().write(result.toString());
	            
	            //如果是要跳转到某个页面的
	            //new DefaultRedirectStrategy().sendRedirect(request, response, "/main");
	            return;
			}
		};
	}
	
	/**
	 * loginFailureHander 登录失败
	 * @return
	 */
	public SimpleUrlAuthenticationFailureHandler loginFailureHander() {
		return new SimpleUrlAuthenticationFailureHandler() {
			@Override
			public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
					AuthenticationException exception) throws IOException, ServletException {
				// 以Json格式返回
				JSONObject result = new JSONObject();
				result.put("returnCode", "201");
				result.put("returnMsg", "登录失败");
				//response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
				response.setContentType("application/json");
				response.setCharacterEncoding("UTF-8");
				response.getWriter().write(result.toString());
				return;
			}

		};
	}
	
	/**
	 * logoutSuccessHandler 登出成功处理
	 * 
	 * @return
	 */
	@Bean
	public LogoutSuccessHandler logoutSuccessHandler() {
		return new LogoutSuccessHandler() {
			@Override
			public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
					Authentication authentication) throws IOException, ServletException {
				try {
					UserSecurity user = (UserSecurity) authentication.getPrincipal();
					logger.info("USER : " + user.getUsername() + " LOGOUT SUCCESS !  ");
				} catch (Exception e) {
					logger.info("LOGOUT EXCEPTION , e : " + e.getMessage());
				}
				String contextPath = httpServletRequest.getContextPath();
				httpServletResponse.sendRedirect(contextPath+"/login");
			}
		};
	}

	/**
     * 表达式控制器
     * 用于freemarker页面的security标签的权限校验
	 * 如：
	 *    <@security.authorize access="hasPermission('user', 'read')">
	 *        <a class="layui-btn layui-btn-normal addGroup_btn">添加文章</a>
	 * 	  </@security.authorize>
     * @return
     */
	@Bean
	public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() {
        DefaultWebSecurityExpressionHandler webSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
		CustomizePrimissionEvaluator primissionEvaluator = new CustomizePrimissionEvaluator();
        webSecurityExpressionHandler.setPermissionEvaluator(primissionEvaluator);
        return webSecurityExpressionHandler;
    }
}
